﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using alib.BitArray;
using alib.Enumerable;
using alib.Debugging;
using alib.Collections.ReadOnly;

using alib.Collections;
using alib.Concurrency;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq} ({InstanceType._feature_info(),nq})")]
	public abstract class Rule : StaticExpandEntry, IChartLicense, IActiveRule
	{
		protected Rule(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
			i_key_daughter = -1;
		}

		/// find the first KEY daughter, i.e. the first daughter where the type at the KEY daughter path (if
		/// specified) matches the KEY daughter type (if specified). In the absence of such a match, the
		/// parser will default to active rightwards expansion starting at daughter index 0.
		/// As noted in a comment elsewhere, the KEY daughter index remains constant for active edges derived
		/// from this rule, so we don't need to redo this during parsing; instead we can just copy this value
		/// over.
		protected override Tfs EnsureExpanded()
		{
			var tfs = base.EnsureExpanded();

			var dn = tfs[im.RuleArgsPath].ListSlots;
			if (dn == null)
				throw new Exception("Could not find daughters in rule TFS.");

			int ikd = 0;
			var t_key = im.tt_key_daughter;
			if (t_key != 0)
			{
				for (int i = 0; i < dn.Length; i++)
					if (dn[i][im.KeyDaughterPath].EdgeFlag == t_key)
					{
						ikd = i;
						/// If multiple daughters have KEY-ARG marked, it is not clear what to do. This
						/// implementation currently elects to take the leftmost acceptable KEY:
						break;
					}
			}
			_dtr_nodes = dn;
			i_key_daughter = ikd;

			var rnp = im.RnamePath;
			if (rnp == null || rnp.IsEmpty || (_rname = tfs[rnp].EdgeFlag) <= 0)
				_rname = td.ΔString.EdgeFlag;

			return tfs;
		}

		int i_key_daughter;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int KeyDaughterIndex
		{
			[DebuggerStepThrough]
			get
			{
				Tfs _;
				if (i_key_daughter == -1)
					_ = base.Expanded;
				return i_key_daughter;
			}
		}

		TfsSlot[] _dtr_nodes;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsSlot[] DaughterNodes
		{
			[DebuggerStepThrough]
			get
			{
				Tfs _;
				if (_dtr_nodes == null)
					_ = base.Expanded;
				return _dtr_nodes;
			}
		}

		Edge.Flag _rname;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Edge.Flag MonikerFlag
		{
			[DebuggerStepThrough]
			get
			{
				Tfs _;
				if (_rname == 0)
					_ = base.Expanded;
				return _rname;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String MonikerString
		{
			[DebuggerStepThrough]
			get
			{
				var rn = this.MonikerFlag;
				return rn > 0 ? tu.TypeNameOrStringValue(rn) : String.Empty;
			}
		}

		bool f_span_only = false;
		public void _set_span_only() { f_span_only = true; }
		public bool IsSpanOnly { get { return f_span_only; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Check rule compatibility. There are two cache directions: first, each rule pre-checks and caches the mother
		/// rules for which this rule is compatible with that mother's KEY daughter. This cache is used by both grammar 
		/// rules--which build both upwards and downwards--and lexical rules--which build upwards only. SyntaxRule 
		/// subclasses this function to add the down cache for non-key-daughters.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public virtual void AnalyzeRuleCompatibility(TfsUnifier uc, IReadOnlyList<Rule> key_mother_check, IReadOnlyList<Rule> daughter_check)
		{
			/// find the mother rules for which this rule is compatible with its KEY daughter
			Debug.Assert(_key_mothers == null);
			_key_mothers = new BitArr(em._rules.Count);

			var tfs = base.Expanded;

			foreach (Rule mother in key_mother_check)
				if (uc.UnifyCheck(tfs, mother.DaughterNodes[mother.KeyDaughterIndex]))
					_key_mothers[mother.RuleIndex] = true;
		}

		public int RuleIndex { get { return em._rules.IndexOf(this); } }

		protected BitArr _key_mothers;

		public bool IsCompatibleKeyMother(Rule r) { return _key_mothers == null || _key_mothers[r.RuleIndex]; }

		//////////////////////////////////////////////////////////////////////
		/// IDaughterArity
		//////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int DaughterArity { [DebuggerStepThrough] get { return DaughterNodes.Length; } }

		//////////////////////////////////////////////////////////////////////
		/// IActiveRule
		//////////////////////////////////////////////////////////////////////

		public Rule BaseRule { get { return this; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsSlot CContSlot { get { return this.Expanded[mrsm.ifeat_ccont]; } }

		//////////////////////////////////////////////////////////////////////
		/// 
		//////////////////////////////////////////////////////////////////////
		public IEnumerable<GenRuleSkol> SkolemizeFor(GenerationInput input)
		{
			foreach (var skr in input.RelMatchHelper.EnumerateSkolemizationResults(input, this.CContSlot))
			{
				var grs = new GenRuleSkol(this, skr);

				//ctrl.bp("skolemized rules", grs);

				yield return grs;
			}
		}

		//////////////////////////////////////////////////////////////////////
		/// 
		//////////////////////////////////////////////////////////////////////
		public override String ToString()
		{
			return String.Format("{0}", Name);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// rule compatibility pre-check
	/// 
	/// Required property init:
	///		EntryMGr
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class RuleCompatibiltyCache : has_grammar_base, ISupportInitializeNotification
	{
		public RuleCompatibiltyCache(IRtParent parent, Grammar g)
			: base(parent, g)
		{
			/// Configuration Defaults:
			this._ena = true;
		}

		public RuleCompatibiltyCache(IRtParent parent)
			: this(parent, default(Grammar))
		{
		}

		public void EndInit()
		{
			if (!_ena)
				return;

			if (Interlocked.CompareExchange(ref em.mre_rule_cache, new ManualResetEvent(false), Tasks.Unclaimed) == Tasks.Unclaimed)
				Task.Factory.StartNew(_action);
		}

		void _action()
		{
#if true
			var arr = em._rules;
			for (int i = 0; i < arr.Count; i++)
				_check_rule(arr[i]);
#else
			TaskControl.ParallelLoop(em._rules, _check_rule);
#endif
			Interlocked.Exchange(ref em.mre_rule_cache, null).Set();
		}

		///////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////
		void _check_rule(Rule r)
		{
			if (r is LexicalRule)
				r.AnalyzeRuleCompatibility(fsu, em._rules, null);
			else if (r is SyntaxRule)
				r.AnalyzeRuleCompatibility(fsu, em._syntax_rules, em._rules);
		}

		bool _ena;
		public bool Enable
		{
			get { return this._ena; }
			set { this._ena = value; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class SyntaxRule : Rule
	{
		public SyntaxRule(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
		}

		BitArr[] _non_key_daughters;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override void AnalyzeRuleCompatibility(TfsUnifier uc, IReadOnlyList<Rule> key_mother_check, IReadOnlyList<Rule> daughter_check)
		{
			Debug.Assert(_non_key_daughters == null);

			base.AnalyzeRuleCompatibility(uc, key_mother_check, daughter_check);

			TfsSlot[] rg_dtr = DaughterNodes;

			/// find the rules which are compatible with each non-KEY daughter position of this rule
			Debug.Assert(_non_key_daughters == null);
			_non_key_daughters = new BitArr[rg_dtr.Length];
			int c_dtrchk = daughter_check.Count;
			for (int i = 0; i < rg_dtr.Length; i++)
			{
				if (i != KeyDaughterIndex)
				{
					BitArr ba = _non_key_daughters[i] = new BitArr(em._rules.Count);
					Rule candidate;
					for (int j = 0; j < c_dtrchk; j++)
						if (uc.UnifyCheck((candidate = daughter_check[j]).Expanded, rg_dtr[i]))
						{
							//Debug.Assert(j == candidate.RuleIndex);
							if (j != candidate.RuleIndex)
								throw new Exception();
							ba[j] = true;
						}
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool CheckDaughterCompatibility(Rule rule, int i_arg)
		{
			return _non_key_daughters == null || _non_key_daughters[i_arg][rule.RuleIndex];
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class LexicalRule : Rule, ILexical
	{
		public LexicalRule(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
			if (this is MorphologicalRule)
			{
				f_spelling_change = 1;
				Interlocked.Increment(ref em._lexical_rules.c_spelling_change);
			}
		}

		LexicalRule[] _lex_key_mothers;

		int f_spelling_change;
		public bool IsSpellingChange { get { return f_spelling_change == 1; } }

		public void SetIsSpellingChange()
		{
			if (f_spelling_change == 1)
				return;
			if (Interlocked.Exchange(ref f_spelling_change, 1) == 0)
				Interlocked.Increment(ref em._lexical_rules.c_spelling_change);
		}

		public LexicalRule[] NonAffixingCompatibleMothers
		{
			get
			{
				if (_lex_key_mothers == null)
				{
					if (_key_mothers == null || _key_mothers.IsAllZeros)
						_lex_key_mothers = alib.Collections.Collection<LexicalRule>.None;
					else
					{
						_lex_key_mothers = _key_mothers
											.OnesPositions()
											.Select(ix => em._rules[ix])
											.OfExactType<LexicalRule>()
											.ToArray();
					}
				}
				return _lex_key_mothers;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public String morph_word(String mwe_rep)
		{
			Irregulars.RuleSurf[] irregs;
			var irx = em.lex.mph.irregs;
			if (irx != null && irx.irreg_rev != null && irx.irreg_rev.TryGetValue(mwe_rep, out irregs))
				foreach (var ir in irregs)
					if (ir.rule == this)
						return ir.surf;

			MorphologicalRule mr = this as MorphologicalRule;
			MorphologySubrule sr;
			if (mr != null)
				for (int i = mr.Subrules.Length - 1; i >= 0; i--)
					if ((sr = mr.Subrules[i]).rev_regex.IsMatch(mwe_rep))
						return sr.rev_regex.Replace(mwe_rep, sr.rev_replace);

			return null;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class MorphologicalRule : LexicalRule
	{
		MorphologySubrule[] morph_subrules = null;

		public MorphologicalRule(Grammar g, Type t, ConsDefs consdef, List<MorphologySubrule> morph_subrules)
			: base(g, t, consdef)
		{
			Interlocked.Increment(ref em._lexical_rules.c_morphological);

			if (morph_subrules != null)
				this.morph_subrules = morph_subrules.ToArray();
		}

		public MorphologySubrule[] Subrules { get { return morph_subrules; } }

		public override String ToString()
		{
			return base.ToString() + String.Format("  subrules: {0}", morph_subrules.Length);
		}
	};
}
